#include "HFFDecode.h"
#include <thread>
#include "hlog.h"
#include "hscope.h"
#include "htime.h"

extern "C"
{
#include<libavcodec/avcodec.h>
}

#include <iostream>
using namespace std;

void XFreePacket(AVPacket **pkt)
{
	if (!pkt || !(*pkt))return;
	av_packet_free(pkt);
}


void XFreeFrame(AVFrame **frame)
{
	if (!frame || !(*frame))return;
	av_frame_free(frame);
}


static double r2d(AVRational r)
{
	return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}


void HFFDecode::Close()
{
	hlogi("HFFDecode::Close()");
	m_mux.lock();
	if (codec_ctx)
	{
		avcodec_close(codec_ctx);
		avcodec_free_context(&codec_ctx);
	}
	pts = 0;

	if(para)
	{
		avcodec_parameters_free(&para);
	}

	m_mux.unlock();
}


void HFFDecode::Clear()
{
	m_mux.lock();
	//清理解码缓冲
	if (codec_ctx)
		avcodec_flush_buffers(codec_ctx);
    
	m_mux.unlock();
}


//打开解码器
bool HFFDecode::Open()
{
	hlogi("HFFDecode::Open()");
	if (!para) return false;

	bool ret = true;

	m_mux.lock();
	if (codec_ctx)
	{
		avcodec_close(codec_ctx);
		avcodec_free_context(&codec_ctx);
	}
	pts = 0;

	///解码器打开
	///找到解码器
  printf("file:%s function:%s line:%d decode_mode = %d\n", __FILE__, __FUNCTION__, __LINE__, decode_mode);
	AVCodec *codec = NULL;
	if (decode_mode != SOFTWARE_DECODE) {
try_hardware_decode:
    printf("file:%s function:%s line:%d decode_mode = %d\n", __FILE__, __FUNCTION__, __LINE__, decode_mode);
    std::string decoder(avcodec_get_name(para->codec_id));
    if (decode_mode == HARDWARE_DECODE_CUVID) {
        decoder += "_cuvid";
        real_decode_mode = HARDWARE_DECODE_CUVID;
    }
    else if (decode_mode == HARDWARE_DECODE_QSV) {
        decoder += "_qsv";
        real_decode_mode = HARDWARE_DECODE_QSV;
    }
    codec = (AVCodec *)avcodec_find_decoder_by_name(decoder.c_str());
    if (codec == NULL) {
        hlogi("Can not find decoder %s", decoder.c_str());
        // goto try_software_decode;
    }
    hlogi("decoder=%s", decoder.c_str());
    printf("file:%s function:%s line:%d decoder=%s\n", __FILE__, __FUNCTION__, __LINE__, decoder.c_str());
  }

  if (codec == NULL) {
try_software_decode:
    codec = (AVCodec *)avcodec_find_decoder(para->codec_id);
    if (codec == NULL) {
      hloge("Can not find decoder %s", avcodec_get_name(para->codec_id));
      m_mux.unlock();
      ret = false;
      return ret;
    }
    real_decode_mode = SOFTWARE_DECODE;
  }

  hlogi("codec_name: %s=>%s", codec->name, codec->long_name);
  printf("file:%s function:%s line:%d codec_name: %s => %s\n", __FILE__, __FUNCTION__, __LINE__, codec->name, codec->long_name);

	codec_ctx = avcodec_alloc_context3(codec);
	if(codec_ctx == NULL)
	{
		hloge("avcodec_alloc_context3");
		m_mux.unlock();
		ret = false;
		return ret;
	}
  defer (if (!ret && codec_ctx) {avcodec_free_context(&codec_ctx); codec_ctx = NULL;})

  // 复制用户数据
  //codec_ctx->flags |= AV_CODEC_FLAG_COPY_OPAQUE;

	///配置解码器上下文参数
	int re = avcodec_parameters_to_context(codec_ctx, para);
	if(re != 0)
	{
		hloge("avcodec_parameters_to_context error: %d", re);
		m_mux.unlock();
		ret =  false;
    return ret;
	}

	//八线程解码
	//codec_ctx->thread_count = 8;

	///打开解码器上下文
	re = avcodec_open2(codec_ctx, codec, 0);
	if (re != 0)
	{
		avcodec_free_context(&codec_ctx);
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);
		hlogi("avcodec_open2  failed! : %s", buf);

		if(real_decode_mode != SOFTWARE_DECODE)
		{
			hlogi("Can not open hardware codec error:%d, try software codec.", re);
			goto try_software_decode;
		}
		
		m_mux.unlock();
		ret = false;
		return ret;
	}
	m_mux.unlock();
	hlogi("avcodec_open2 success!");
  printf("avcodec_open2 success!\n");

	return true;
}


//打开解码器
bool HFFDecode::Open(std::string decoder)
{
	hlogi("HFFDecode::Open()");

	bool ret = true;

	m_mux.lock();
	if (codec_ctx)
	{
		avcodec_close(codec_ctx);
		avcodec_free_context(&codec_ctx);
	}
	pts = 0;

	///解码器打开
	///找到解码器
  printf("file:%s function:%s line:%d decode_mode = %d\n", __FILE__, __FUNCTION__, __LINE__, decode_mode);
	AVCodec *codec = NULL;
	if (decode_mode != SOFTWARE_DECODE) {
try_hardware_decode:
    printf("file:%s function:%s line:%d decode_mode = %d\n", __FILE__, __FUNCTION__, __LINE__, decode_mode);
    if (decode_mode == HARDWARE_DECODE_CUVID) {
        decoder += "_cuvid";
        real_decode_mode = HARDWARE_DECODE_CUVID;
    }
    else if (decode_mode == HARDWARE_DECODE_QSV) {
        decoder += "_qsv";
        real_decode_mode = HARDWARE_DECODE_QSV;
    }
    codec = (AVCodec *)avcodec_find_decoder_by_name(decoder.c_str());
    if (codec == NULL) {
        hlogi("Can not find decoder %s", decoder.c_str());
        // goto try_software_decode;
    }
    hlogi("decoder=%s", decoder.c_str());
    printf("file:%s function:%s line:%d decoder=%s\n", __FILE__, __FUNCTION__, __LINE__, decoder.c_str());
  }

  if (codec == NULL) {
try_software_decode:
    codec =  (AVCodec *)avcodec_find_decoder_by_name(decoder.c_str());
    if (codec == NULL) {
      hloge("Can not find decoder %s", decoder.c_str());
      m_mux.unlock();
      ret = false;
      return ret;
    }
    real_decode_mode = SOFTWARE_DECODE;
  }

  hlogi("codec_name: %s=>%s", codec->name, codec->long_name);
  printf("file:%s function:%s line:%d codec_name: %s => %s\n", __FILE__, __FUNCTION__, __LINE__, codec->name, codec->long_name);

	codec_ctx = avcodec_alloc_context3(codec);
	if(codec_ctx == NULL)
	{
		hloge("avcodec_alloc_context3");
		m_mux.unlock();
		ret = false;
		return ret;
	}
  defer (if (!ret && codec_ctx) {avcodec_free_context(&codec_ctx); codec_ctx = NULL;})

  // 复制用户数据
  //codec_ctx->flags |= AV_CODEC_FLAG_COPY_OPAQUE;

	///配置解码器上下文参数
	// int re = avcodec_parameters_to_context(codec_ctx, para);
	// if(re != 0)
	// {
	// 	hloge("avcodec_parameters_to_context error: %d", re);
	// 	m_mux.unlock();
	// 	ret =  false;
  //   return ret;
	// }

	//八线程解码
	//codec_ctx->thread_count = 8;
  codec_ctx->pix_fmt  = AV_PIX_FMT_YUV420P;

	///打开解码器上下文
	int re = avcodec_open2(codec_ctx, 0, 0);
	if (re != 0)
	{
		avcodec_free_context(&codec_ctx);
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);
		hlogi("avcodec_open2  failed! : %s", buf);

		if(real_decode_mode != SOFTWARE_DECODE)
		{
			hlogi("Can not open hardware codec error:%d, try software codec.", re);
			goto try_software_decode;
		}
		
		m_mux.unlock();
		ret = false;
		return ret;
	}
	m_mux.unlock();
	hlogi("avcodec_open2 success!");
  printf("avcodec_open2 success!\n");

	return true;
}


//发送到解码线程，不管成功与否都释放pkt空间（对象和媒体内容）
bool HFFDecode::Send(AVPacket *pkt)
{
	//容错处理
	if (!pkt || pkt->size <= 0 || !pkt->data) return false;
	m_mux.lock();
	if (!codec_ctx)
	{
		m_mux.unlock();
		return false;
	}
  //printf("file:%s function:%s line:%d time5 = %lld\n", __FILE__, __FUNCTION__, __LINE__, gethrtime_us() / 1000);
	int re = avcodec_send_packet(codec_ctx, pkt);
  //printf("file:%s function:%s line:%d time6 = %lld\n", __FILE__, __FUNCTION__, __LINE__, gethrtime_us() / 1000);
	m_mux.unlock();
	av_packet_free(&pkt);
	if (re != 0)return false;
	return true;
}


//获取解码数据，一次send可能需要多次Recv，获取缓冲中的数据Send NULL在Recv多次
//每次复制一份，由调用者释放 av_frame_free
AVFrame* HFFDecode::Recv()
{
	m_mux.lock();
	if (!codec_ctx)
	{
		m_mux.unlock();
		return NULL;
	}
	AVFrame *frame = av_frame_alloc();
	int re = avcodec_receive_frame(codec_ctx, frame);
	m_mux.unlock();
	if (re != 0)
	{
		av_frame_free(&frame);
		return NULL;
	}
	
	//pts = frame->pts*(1000 * (r2d(m_time_base)));

  cout << "frame->pts:" << frame->pts <<endl;

	return frame;
}


HFFDecode::HFFDecode()
{
	hlogi("HFFDecode::HFFDecode()");
	decode_mode = DEFAULT_DECODE_MODE;
	real_decode_mode = DEFAULT_DECODE_MODE;
}


HFFDecode::~HFFDecode()
{
	hlogi("HFFDecode::~HFFDecode()");
}
